import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.table.AbstractTableModel;

import org.apache.log4j.Logger;

import pl.edu.pw.elka.dzielins.revanalyzer.Project;
import pl.edu.pw.elka.dzielins.revanalyzer.instance.InstanceObject;
import pl.edu.pw.elka.dzielins.revanalyzer.instance.InstanceRelation;
import pl.edu.pw.elka.dzielins.revanalyzer.model.ModelObject;
import pl.edu.pw.elka.dzielins.revanalyzer.model.ModelRelation;


public class RevanalyzerTableModel extends AbstractTableModel {
	
	private static final Logger LOG = Logger.getLogger(RevanalyzerTableModel.class);
	private ModelObject mo;
	private List<InstanceObject> ios;
	private ModelRelation mr;
	private List<InstanceRelation> irs;
	private Project project;
	
	public RevanalyzerTableModel(Project project, ModelObject modelObject) {
		this.project = project;
		this.mo = modelObject;
		
		ios = project.getInstanceObjectsFromModelObject(mo);
		LOG.debug(String.valueOf(ios));		
	}

	public RevanalyzerTableModel(Project project, ModelRelation modelRelation) {
		this.project = project;
		this.mr = modelRelation;
		
		// get instances of owner object
		List<InstanceObject> owners = project.getInstanceObjectsFromModelObject(mr.getOwner());
		// get all relation instances
		irs = new LinkedList<InstanceRelation>();
		for (InstanceObject io : owners) {
			// TODO exceptions
			Map<ModelRelation,Set<InstanceRelation>> iors = io.getRelations();
			if (iors != null && iors.get(mr) != null && !iors.get(mr).isEmpty())
				irs.addAll(iors.get(mr));
		}
		LOG.debug(String.valueOf(irs));
	}
	
	public void addNewRow() {
		// relation
		if (mo == null && mr != null) {
			// get random element to be relation owner
			List<InstanceObject> list = project.getInstanceObjectsFromModelObject(mr.getOwner(), true);
			if (list == null || list.isEmpty())
				return;
			InstanceObject io = list.get(0);
			// get random arguments
			List<InstanceObject> ios = new ArrayList<InstanceObject>(mr.getArguments().size());
			for (ModelObject modelObject : mr.getArguments()) {
				list = project.getInstanceObjectsFromModelObject(modelObject, true);
				ios.add(list.get(0));
			}
			InstanceRelation ir = new InstanceRelation(io, mr.getName(), mr, ios.toArray(new InstanceObject[] {}));
			io.addRelation(ir);
			irs.add(ir);
		}
		// object
		else if (mo != null && mr == null) {
			// create new instance object
			InstanceObject io = new InstanceObject("New" + mo.getName(), mo);
			// add instance object to project
			project.getInstance().getObjects().add(io);
			// add object to model list
			ios.add(io);
		}
		super.fireTableDataChanged();
	}
	
	public void removeRow(int rowIndex) {
		// relation
		if (mo == null && mr != null && rowIndex <= irs.size()) {
			// delete relation from owner
			InstanceRelation ir = irs.get(rowIndex);
			InstanceObject owner = ir.getOwner();
			owner.removeRelation(ir);
			// delete relation from model list
			irs.remove(rowIndex);
		}
		// object
		else if (mo != null && mr == null && rowIndex <= ios.size()) {
			// delete object from instance
			project.getInstance().removeObject(ios.get(rowIndex));
			// delete object from model list
			ios.remove(rowIndex);
		}
		super.fireTableDataChanged();
	}

	@Override
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		LOG.debug("Saving...");
		// relation
		if (mo == null && mr != null && rowIndex <= irs.size()) {
			InstanceRelation ir = irs.get(rowIndex);
			if (columnIndex <= ir.getArgumentsWithOwner().size()) {
				// relation owner
				if (columnIndex == 0) {
					// get old owner and new owner
					InstanceObject oldOwner, newOwner;
					newOwner = (InstanceObject) aValue;
					oldOwner = ir.getOwner();
					// remove relation from old owner
					oldOwner.removeRelation(ir);
					// change the owner
					ir.setOwner((InstanceObject) aValue);
					// add relation to new owner
					newOwner.addRelation(ir);
				}
				// non-owner argument
				else {
					// just change the argument
					ir.getArguments().set(columnIndex-1, (InstanceObject) aValue);
				}
			}
		}
		// object
		else if (mo != null && mr == null && columnIndex == 0 && rowIndex <= ios.size()) {
			InstanceObject io = ios.get(rowIndex);
			LOG.debug(io);
			io.setName((String) aValue);
			LOG.debug(io);
			LOG.debug(project.getInstance().toString());
		}
		super.setValueAt(aValue, rowIndex, columnIndex);
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return true;
	}

	@Override
	public String getColumnName(int column) {
		// relation
		if (mo == null && mr != null) {
			String s = "Argument " + String.valueOf(column + 1);
			if (column == 0)
				s += " (Owner)";
			return s;		
		} 
		// object
		else if (mo != null && mr == null)
			return "Elements";
		else
			return super.getColumnName(column);
	}

	@Override
	public int getColumnCount() {
		// relation
		if (mo == null && mr != null)
			return mr.getArgumentsWithOwner().size();
		// object
		else if (mo != null && mr == null)
			return 1;
		else return 0;
	}

	@Override
	public int getRowCount() {
		// relation
		if (mo == null && mr != null && irs != null)
			return irs.size();
		// object
		else if (mo != null && mr == null && ios != null)
			return ios.size();
		return 0;
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		// relation
		if (mo == null && mr != null) {
			if (columnIndex >= getColumnCount())
				return null;
			InstanceRelation ir = irs.get(rowIndex);
			return ir.getArgumentsWithOwner().get(columnIndex);
		} 
		// object
		else if (mo != null && mr == null) {
			if (columnIndex != 0 )
				return null;
			return ios.get(rowIndex);
		}
		return null;
	}

}
